#ifndef PROCESSVIEW_H_
#define PROCESSVIEW_H_

#include <list>
#include <boost/shared_ptr.hpp>

#include "conf/node.h"
#include "conf/attribute.h"
#include "widgets.h"
#include "panel.h"

namespace gpyp {

class Recorder;
typedef boost::shared_ptr<Recorder> RefRecorder;

class ProcessViewElement : public panel::PagePanedElement {
private:
  conf::RefAttributeInt  detach_delay_;
  conf::RefAttributeBool show_proc_name_;

public:
  inline conf::RefAttributeInt detach_delay() {
    return detach_delay_;
  }
  inline conf::RefAttributeBool show_proc_name() {
    return show_proc_name_;
  }

  inline ProcessViewElement(const Glib::ustring &name, bool enable) :
    panel::PagePanedElement(name, enable),
    detach_delay_          ("detach_delay",   3),
    show_proc_name_        ("show_proc_name", true)
  {
    add(detach_delay_);
    add(show_proc_name_);
  }
};
typedef boost::shared_ptr<ProcessViewElement> RefProcessViewElement;

class ProcessView : public panel::PagePaned<Gtk::HPaned> {
  friend class Manager;
private:
  class Manager : boost::noncopyable {
    friend class ProcessView;
  private:
    ProcessView                    &view_;
    Gtk::TreeIter                   treeiter_;
    RefProcess                      proc_;
    RefRecorder                     recorder_;
    bool                            special_;

    Glib::RefPtr<Gtk::TextBuffer>   textbuffer_;

    sigc::connection                conn_run_;
    sigc::connection                conn_dead_;
    sigc::connection                conn_error_;
    sigc::connection                conn_stdout_;
    sigc::connection                conn_stderr_;

    sigc::connection                conn_recorder_new_process_;
    sigc::connection                conn_recorder_stop_;

    sigc::connection                conn_ddetach_timeout_;

  private:
    void on_output(const Glib::ustring       &text,
                   Glib::RefPtr<Gtk::TextTag> tag);

    void on_run();
    void on_dead();
    void on_error();
    void on_stdout(const std::string &text);
    void on_stderr(const std::string &text);

    void proc_signals_connect();
    void proc_signals_disconnect();

    void recorder_on_new_process(RefProcess proc);
    void recorder_on_stop();

    bool delayed_detach_timeout();

  public:
    inline RefProcess get() {
      return proc_;
    }
    RefChannel get_channel();

    void detach();
    void detach_delayed(bool check_selected = false);
    void detach_cancel();

    Manager(ProcessView  &view,
            Gtk::TreeIter tree_iter,
            RefProcess    proc,
            bool          special = false);
    Manager(ProcessView  &view,
            Gtk::TreeIter tree_iter,
            RefRecorder   recorder);
    virtual ~Manager();
  };
  typedef boost::shared_ptr<Manager> RefManager;

  class List : public WidgetInput<TreeView> {
    friend class ProcessView;
    friend class Manager;
  private:
    static const Gdk::Color COLOR_DEAD;

  private:
    RefProcessViewElement           conf_;

    typedef Gtk::TreeModelColumn<RefManager> TreeModelColumnProcess;
    typedef Gtk::TreeModelColumn<Glib::ustring> TreeModelColumnString;
    TreeModelColumnProcess          model_column_process_;
    Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > model_column_icon_;
    Gtk::TreeModelColumn<Glib::ustring> model_column_name_;
    Gtk::TreeModelColumn<Glib::ustring> model_column_chname_;
    Gtk::TreeModelColumn<Pango::Underline> model_column_underline_;
    Gtk::TreeModelColumnRecord      model_columns_;
    Glib::RefPtr<Gtk::ListStore>    liststore_;

    Gtk::CellRendererPixbuf         renderer_icon_;
    Gtk::CellRendererText           renderer_name_;
    Gtk::CellRendererText           renderer_chname_;
    Gtk::CellRendererText           renderer_pid_;

    Gtk::TreeViewColumn             column_icon_;
    Gtk::TreeViewColumn             column_name_;
    Gtk::TreeViewColumn             column_chname_;
    Gtk::TreeViewColumn             column_pid_;

    Menu                            process_menu_;

  protected:
    RefManager get_manager(const Gtk::TreeIter &iter);
    RefProcess get_proc(const Gtk::TreeIter &iter);

    void set_cell_common_data(Gtk::CellRenderer *cell, const Gtk::TreeIter &iter);
    void pid_on_cell_data(Gtk::CellRenderer *cell, const Gtk::TreeIter &iter);

    void prepare_commandopts(CommandOpts &opts);
    bool on_popup(guint button, guint32 activate_time);

  public:
    Glib::RefPtr<Gtk::ListStore> liststore();

    RefManager get_selected();

    List(Application &app, RefProcessViewElement conf);
  };

private:
  Application                  &app_;
  RefProcessViewElement         conf_;

  List                          list_;
  TextView                      outputview_;

  sigc::connection              conn_outputview_vadjustment_changed_;

  RefManager                    proc_peercast_;
  RefManager                    proc_pcraw_proxy_;

  Glib::RefPtr<Gtk::TextBuffer> textbuffer_;
  Glib::RefPtr<Gtk::TextTag>    tag_start_;
  Glib::RefPtr<Gtk::TextTag>    tag_end_;
  Glib::RefPtr<Gtk::TextTag>    tag_stdout_;
  Glib::RefPtr<Gtk::TextTag>    tag_stderr_;

  RefManager                    selected_;

  BuiltinCommand               &bc_processview_;

  size_t                        reset_model_count_;

  sigc::connection              conn_commandrunner_new_process_;
  sigc::connection              conn_commandrunner_run_peercast_;
  sigc::connection              conn_commandrunner_run_pcraw_proxy_;
  sigc::connection              conn_commandrunner_record_;

protected:
  RefManager find(RefProcess proc);

  void erase(Gtk::TreeIter &iter);
  RefManager add_process(RefProcess proc, bool special);

  void check_reset_model();

  void commandrunner_on_new_process(RefProcess proc);
  void commandrunner_on_run_peercast(RefProcess proc);
  void commandrunner_on_run_pcraw_proxy(RefProcess proc);
  void commandrunner_on_record(RefRecorder recorder);

  void list_selection_on_changed();
  void outputview_on_set_scroll_adjustments(Gtk::Adjustment *hadjustment,
                                            Gtk::Adjustment *vadjustment);
  void outputview_vadjustment_on_changed();

public:
  void detach(RefProcess proc);

  ProcessView(Application &app, RefProcessViewElement conf);
  virtual ~ProcessView();
};

}/*gpyp*/

#endif/*PROCESSVIEW_H_*/
